--'****************************************************** --'* Биндер объекта зоны аномалий . --'****************************************************** -- -- Modified by Tronex -- 2019/5/23 -- -- Added a new function (refresh) that acts as an update check for artefacts. -- no need to allow anomaly zones to constantly update for artefacts check. this function gets called instead when its needed -- 2019/8/22 artefact list is exported to a config --------------------------------------------------------- local enable_debug = false function print_debug(fmt, ...) if enable_debug then printf("bind_anomal_zone | " .. fmt, ...) end end artefact_ways_by_id = {} artefact_points_by_id = {} parent_zones_by_artefact_id = {} local psi_storm_chance local surge_chance local arty_collection = {} local function actor_on_first_update() local lvl_name = level.name() local m_data = alife_storage_manager.get_state() -- check if artefacts can respawn for this level if (m_data.artefact_respawn_levels and m_data.artefact_respawn_levels[lvl_name] == true) then m_data.artefact_respawn_levels[lvl_name] = nil force_spawn_artefacts() end end local function actor_on_interaction(typ, obj, name) if (typ == "anomalies") and (name == "emission_end" or name == "psi_storm_end") then local m_data = alife_storage_manager.get_state() if not (m_data.artefact_respawn_levels) then m_data.artefact_respawn_levels = {} end -- All other levels can spawn artefacts once player travel to them local level_name local level_name_curr = level.name() local gg,gini,sim = game_graph(),game_ini(),alife() for lvl in gg:levels() do level_name = sim:level_name(lvl.id) local weather = level_name and gini:r_string_ex(level_name,"weathers") if (weather and weather ~= "indoor") and (level_name ~= level_name_curr) then m_data.artefact_respawn_levels[level_name] = true print_debug("Level [%s] will spawn artefacts",level_name) end end -- Spawn artefacts in current level force_spawn_artefacts() end end function on_game_start() local ini = ini_file("items\\settings\\artefacts.ltx") psi_storm_chance = ini:r_float_ex("settings","psi_storm_chance") or 0.7 surge_chance = ini:r_float_ex("settings","surge_chance") or 1.4 local result, id, value = "", "", "" local result2, id2, value2 = "", "", "" local n = ini:line_count("artefact_groups") for i=0,n-1 do result, id, value = ini:r_line_ex("artefact_groups",i,"","") if ini:section_exist(id) then arty_collection[id] = {} local m = ini:line_count(id) local cnt = 0 for j=0,m-1 do result2, id2, value2 = ini:r_line_ex(id,j,"","") if id2 and ini_sys:section_exist(id2) then cnt = cnt + 1 arty_collection[id][cnt] = id2 print_debug("artefact list | %s <- %s", id, id2) else printe("!ERROR bind_anomaly_zone | incorrect section name: %s", id2) end end end end --[[ local function on_game_load() ui_debug_launcher.inject( "action" , { name = "Spawn artefacts" , functor = {bind_anomaly_zone.force_spawn_artefacts} , hide_ui = 0 } ) end RegisterScriptCallback("on_game_load",on_game_load) --]] RegisterScriptCallback("actor_on_interaction",actor_on_interaction) RegisterScriptCallback("actor_on_first_update",actor_on_first_update) end ------------------------------------- function force_spawn_artefacts() local anomalies = db.anomaly_by_name for k,v in pairs(anomalies) do v:respawn_artefacts_and_replace_anomaly_zone() print_debug("respawn artefacts in anomal zone [%s]", tostring(k)) end end function bind(obj) obj:bind_object(anomaly_zone_binder(obj)) end local anom_sect = "anomal_zone" class "anomaly_zone_binder" (object_binder) function anomaly_zone_binder:__init(obj) super(obj) local spawn_ini = obj:spawn_ini() local filename = spawn_ini and spawn_ini:r_string_ex(anom_sect,"cfg") if not filename then printf("WARNING: anomaly_zone_binder: 'anomal_zone' section missing from spawn ini") self.disabled = true return end self.ini = ini_file(filename) if not (self.ini) then printf("WARNING: anomaly_zone_binder: not setup properly! can't load cfg %s",filename) self.disabled = true return end self.artefact_ways_by_id = {} self.artefact_points_by_id = {} self.disabled = false self.turned_off = false self.artefacts_table = {} self.start_artefacts_table = {} self.artefacts_coeff_table = {} self.path_table = {} self.fields_table = {} self.mines_table = {} self.respawn_tries_table = {} self.max_artefacts_table = {} self.forces_table = {} self.spawned_count = 0 self.s_chance = 1 self.respawn_artefacts = math.random() <= 0.10 self.forced_spawn = false self.forced_spawn_override = false self.forced_artefact = "" self.layers_count = self.ini:r_float_ex(anom_sect,"layers_count") or 1 self.cur_layer = "layer_"..math.random(1,self.layers_count) self.custom_placement = self.layers_count>1 local def_respawn_tries = self.ini:r_float_ex(anom_sect,"respawn_tries") or 1 local def_max_artefacts = self.ini:r_float_ex(anom_sect,"max_artefacts") or 1 local def_app_force_xz = self.ini:r_float_ex(anom_sect,"applying_force_xz") or 200 local def_app_force_y = self.ini:r_float_ex(anom_sect,"applying_force_y") or 400 local def_arts = self.ini:r_string_ex(anom_sect,"artefacts") local def_start_arts = self.ini:r_string_ex(anom_sect,"start_artefact") local def_ways = self.ini:r_string_ex(anom_sect,"artefact_ways") local def_field_name = self.ini:r_string_ex(anom_sect,"field_name") local def_coeff_sect_name = self.ini:r_string_ex(anom_sect,"coeffs_section") or "coeff" local def_coeffs = self.ini:r_string_ex(anom_sect,"coeff") local section, arts, start_arts, parsed_condlist, coeffs_sect_name, coeffs, path, field, mines_section for i = 1, self.layers_count do section = "layer_"..i self.respawn_tries_table[section] = self.ini:r_float_ex(section,"respawn_tries") or self.ini:r_float_ex(section,"artefact_count") or def_respawn_tries self.max_artefacts_table[section] = self.ini:r_float_ex(section,"max_artefacts") or def_max_artefacts self.forces_table[section] = {} self.forces_table[section].xz = self.ini:r_float_ex(section,"applying_force_xz") or def_app_force_xz self.forces_table[section].y = self.ini:r_float_ex(section,"applying_force_y") or def_app_force_y arts = self.ini:r_string_ex(section,"artefacts") or def_arts if arts == nil then printf("There is no field 'artefacts' in section [%s] in obj [%s]", section, obj:name()) end self.artefacts_table[section] = parse_names(arts) start_arts = self.ini:r_string_ex(section,"start_artefact") or def_start_arts if start_arts ~= nil then self.forced_spawn = true self.start_artefacts_table[section] = parse_names(start_arts) end coeffs_sect_name = def_coeff_sect_name parsed_condlist = self.ini:r_string_to_condlist(section,"coeffs_section") if (parsed_condlist) then local str = xr_logic.pick_section_from_condlist(get_story_object("actor"), nil, parsed_condlist) if (str and str ~= "" and str ~= "nil") then coeffs_sect_name = str end end coeffs = self.ini:r_string_ex(section,coeffs_sect_name) or def_coeffs if coeffs ~= nil then self.artefacts_coeff_table[section] = parse_nums(coeffs) else self.artefacts_coeff_table[section] = {} end path = self.ini:r_string_ex(section,"artefact_ways") or def_ways if path == nil then printf("There is no field 'artefact_ways' in section [%s] in obj [%s]", section, obj:name()) end self.path_table[section] = parse_names(path) if #self.path_table[section] < self.max_artefacts_table[section] then --printf("Not enough ways for anomal zone [%s], in section [%s], must be at least [%s]", tostring(obj:name()), tostring(section), tostring(self.max_artefacts_table[section])) end if(self.custom_placement) then field = self.ini:r_string_ex(section,"field_name") or def_field_name if field == nil then --printf("There is no field 'field_name' in section [%s] in obj [%s]", section, obj:name()) self.fields_table[section] = {} else self.fields_table[section] = parse_names(field)--field end mines_section = self.ini:r_string_ex(section,"mines_section") if mines_section == nil then printf("There is no field 'mines_section' in section [%s] in obj [%s]", section, obj:name()) end if self.ini:line_count(mines_section) == 0 then --printf("There is no 'mines_names' in section [%s] in obj [%s]", mines_section, obj:name()) end local t = empty_table(self.mines_table[section]) if self.ini:line_count(mines_section) > 0 then for i = 0, self.ini:line_count(mines_section)-1 do temp1, mine_name, temp2 = self.ini:r_line(mines_section, i, "", "") t[#t+1] = mine_name end end self.mines_table[section] = t end end self.respawn_tries = self.respawn_tries_table[self.cur_layer] self.max_artefacts = self.max_artefacts_table[self.cur_layer] self.applying_force_xz = self.forces_table[self.cur_layer].xz self.applying_force_y = self.forces_table[self.cur_layer].y end function anomaly_zone_binder:disable_anomaly_fields() if not(self.custom_placement) then self.disabled = true return end local layer = self.cur_layer local anom_fields = bind_anomaly_field.fields_by_names local counter = 0 for k,v in pairs(self.fields_table) do if(k~=layer) then for kk, vv in pairs(self.fields_table[k]) do if(anom_fields[vv]~=nil) then anom_fields[vv]:set_enable(false) else counter = counter + 1 end end end end for k,v in pairs(self.mines_table) do if(k~=layer) then for kk,vv in pairs(self.mines_table[k]) do if(anom_fields[vv]~=nil) then anom_fields[vv]:set_enable(false) else counter = counter + 1 end end end end if(counter==0) then self.disabled = true end if not self.turned_off then for kk, vv in pairs(self.fields_table[layer]) do if(anom_fields[vv]~=nil) then anom_fields[vv]:set_enable(true) end end for kk,vv in pairs(self.mines_table[layer]) do if(anom_fields[vv]~=nil) then anom_fields[vv]:set_enable(true) end end end end function anomaly_zone_binder:respawn_artefacts_and_replace_anomaly_zone() local s_mgr, p_mgr = surge_manager.get_surge_manager(), psi_storm_manager.get_psi_storm_manager() self.s_chance = (s_mgr.started and surge_chance) or (p_mgr.started and psi_storm_chance) or 1 local anom_fields = bind_anomaly_field.fields_by_names self.respawn_artefacts = true if(self.custom_placement) then local layer = self.cur_layer for k,v in pairs(self.fields_table[layer]) do if(anom_fields[v]~=nil) then anom_fields[v]:set_enable(false) end end for k,v in pairs(self.mines_table[layer]) do if(anom_fields[v]~=nil) then anom_fields[v]:set_enable(false) end end layer = "layer_"..math.random(1,self.layers_count) for k,v in pairs(self.fields_table[layer]) do if(anom_fields[v]~=nil) then anom_fields[v]:set_enable(true) end end for k,v in pairs(self.mines_table[layer]) do if(anom_fields[v]~=nil) then anom_fields[v]:set_enable(true) end end self.cur_layer = layer self.respawn_tries = self.respawn_tries_table[self.cur_layer] self.max_artefacts = self.max_artefacts_table[self.cur_layer] self.applying_force_xz = self.forces_table[self.cur_layer].xz self.applying_force_y = self.forces_table[self.cur_layer].y end self:refresh() end function anomaly_zone_binder:spawn_artefact_randomly() local layer = self.cur_layer local rnd_artefact, rnd_artefact_typ if self.forced_spawn_override then rnd_artefact_typ = self.forced_artefact self.forced_spawn_override = false elseif self.forced_spawn then rnd_artefact_typ = self.start_artefacts_table[layer][#self.start_artefacts_table[layer]] self.forced_spawn = false else local spawn_chance = game_difficulties.get_eco_factor("arty_chance") or 0.25 spawn_chance = (spawn_chance * 100) + (game_achievements.has_achievement("geologist") and 5 or 0) spawn_chance = self.s_chance * spawn_chance -- printf("storm arty chance %s", spawn_chance) if math.random(1,100) > spawn_chance then return end local t_layer = self.artefacts_table[layer] rnd_artefact_typ = t_layer[math.random(#t_layer)] --[[ shuffle_table(t_layer) local size = #t_layer local coeff_total = 0 for k, v in pairs(self.artefacts_coeff_table[layer]) do coeff_total = coeff_total + v end if coeff_total == 0 then for i = 1, size do self.artefacts_coeff_table[layer][i] = 1 coeff_total = coeff_total + 1 end end local rnd = math.random(1, coeff_total) for i = 1, size do local chance = self.artefacts_coeff_table[layer][i] if rnd <= chance then rnd_artefact_typ = t_layer[i] break end rnd = rnd - chance end --]] end if not (rnd_artefact_typ and arty_collection[rnd_artefact_typ]) then return end local size_c = #arty_collection[rnd_artefact_typ] rnd_artefact = arty_collection[rnd_artefact_typ][math.random(size_c)] print_debug("artefact [%s] spawned at: %s", rnd_artefact, self.ini:r_string_ex("anomal_zone","field_name") or "") local rnd_path_name = self:get_artefact_path() local rnd_path = patrol(rnd_path_name) local rnd_path_point = math.random(0, rnd_path:count() - 1) local pos = rnd_path:point(rnd_path_point) pos.y = pos.y + 1 -- spawn artefacts 1 meter above the point local artefact_obj = alife_create_item( rnd_artefact , { pos , self.object:level_vertex_id() , self.object:game_vertex_id() }) artefact_ways_by_id[artefact_obj.id] = rnd_path_name artefact_points_by_id[artefact_obj.id] = rnd_path_point self.artefact_ways_by_id[artefact_obj.id] = rnd_path_name self.artefact_points_by_id[artefact_obj.id] = rnd_path_point parent_zones_by_artefact_id[artefact_obj.id] = self self.spawned_count = self.spawned_count + 1 end function anomaly_zone_binder:refresh(from) -- new if (not self.turned_off) and (self.spawned_count < self.max_artefacts) and self.respawn_artefacts then local cnt = self.respawn_tries if cnt > self.max_artefacts - self.spawned_count then cnt = self.max_artefacts - self.spawned_count end if cnt ~= 0 then for i=1, cnt do self:spawn_artefact_randomly() end end self.respawn_artefacts = false print_debug("Anomaly zone [%s] | %s: 1", tostring(self.object:name()), from or "") elseif (not self.turned_off) and (self.spawned_count >= self.max_artefacts) and self.respawn_artefacts then self.respawn_artefacts = false print_debug("Anomaly zone [%s] | %s: 2", tostring(self.object:name()), from or "") end if not(self.disabled) then self:disable_anomaly_fields() end end function anomaly_zone_binder:get_artefact_path() local temp_table = {} local size_t = 0 for k,v in pairs(self.path_table[self.cur_layer]) do local f_spawned = false for kk,vv in pairs(self.artefact_ways_by_id) do if vv ~= nil and v == vv then f_spawned = true end end if not f_spawned then size_t = size_t + 1 temp_table[size_t] = v end end if size_t < 1 then --printf("No free way to spawn artefact in anomal zone [%s]", tostring(self.object:name())) return self.path_table[self.cur_layer][math.random(1, #self.path_table[self.cur_layer])] end local rnd_path_name = temp_table[math.random(1, size_t)] return rnd_path_name end function anomaly_zone_binder:set_forced_override(artefact_name) self.forced_artefact = artefact_name self.forced_spawn_override = true print_debug("set forced override for zone [%s], artefact [%s]", tostring(self.object:name()), tostring(artefact_name)) end function anomaly_zone_binder:reload(section) object_binder.reload(self, section) end function anomaly_zone_binder:reinit() object_binder.reinit(self) db.storage[self.object:id()] = db.storage[self.object:id()] and empty_table(db.storage[self.object:id()]) or {} self.st = db.storage[self.object:id()] end function anomaly_zone_binder:net_spawn(se_abstract) if not object_binder.net_spawn(self, se_abstract) then return false end db.add_anomaly(self) db.add_obj(self.object) self:refresh() return true end function anomaly_zone_binder:net_destroy() db.del_anomaly(self) db.del_obj(self.object) object_binder.net_destroy(self) end function anomaly_zone_binder:update(delta) object_binder.update(self, delta) end function anomaly_zone_binder:cleanup() self:turn_off() self:turn_on() for k,v in pairs(parent_zones_by_artefact_id) do if (v and v.object:id() == self.object:id()) then parent_zones_by_artefact_id[k] = nil end end end function anomaly_zone_binder:turn_off() self.turned_off = true self:disable_anomaly_fields() for k,v in pairs(self.artefact_ways_by_id) do alife_release_id(tonumber(k)) -- artefact_ways_by_id[k] = nil artefact_points_by_id[k] = nil parent_zones_by_artefact_id[k] = nil end self.spawned_count = 0 self.artefact_ways_by_id = {} self.artefact_points_by_id = {} self:refresh() end function anomaly_zone_binder:turn_on(f_af) self.turned_off = false self:disable_anomaly_fields() if f_af then self.respawn_artefacts = true else self.respawn_artefacts = false end self:refresh() end function anomaly_zone_binder:on_artefact_take(obj) local id if(type(obj.id)=="number") then id = obj.id else id = obj:id() end parent_zones_by_artefact_id[id] = nil artefact_ways_by_id[id] = nil artefact_points_by_id[id] = nil self.artefact_ways_by_id[id] = nil self.artefact_points_by_id[id] = nil self.spawned_count = self.spawned_count - 1 if (self.spawned_count < 0) then self.spawned_count = 0 end end -- Standart function for save function anomaly_zone_binder:net_save_relevant() return true end -- Saving anomaly zone function anomaly_zone_binder:save(thread) set_save_marker(thread, "save", false, "anomaly_zone_binder") object_binder.save(self, thread) local count = table.size(self.artefact_ways_by_id) thread:w_u16(count) for k,v in pairs(self.artefact_ways_by_id) do thread:w_u16(k) thread:w_stringZ(v) end count = table.size(self.artefact_points_by_id) thread:w_u16(count) for k,v in pairs(self.artefact_points_by_id) do thread:w_u16(k) thread:w_u8(v) end thread:w_u8(self.spawned_count) thread:w_bool(self.respawn_artefacts) thread:w_bool(self.forced_spawn) thread:w_bool(self.forced_spawn_override) thread:w_stringZ(self.forced_artefact) local layer_num = tonumber(string.sub(self.cur_layer, string.find(self.cur_layer, "_")+1, string.len(self.cur_layer))) if(layer_num) then thread:w_u8(layer_num) else thread:w_u8(255) end thread:w_bool(self.turned_off) set_save_marker(thread, "save", true, "anomaly_zone_binder") end -- Loading anomaly zone function anomaly_zone_binder:load(thread) set_save_marker(thread, "load", false, "anomaly_zone_binder") object_binder.load(self, thread) local count = thread:r_u16() for i=1,count do local art_id = thread:r_u16() local way_name = thread:r_stringZ() artefact_ways_by_id[art_id] = way_name self.artefact_ways_by_id[art_id] = way_name parent_zones_by_artefact_id[art_id] = self end ----------------optimize this--------------------------------------------------- local count = thread:r_u16() for i=1,count do local art_id = thread:r_u16() local point_name = thread:r_u8() artefact_points_by_id[art_id] = point_name self.artefact_points_by_id[art_id] = point_name end -------------------------------------------------------------------------------- self.spawned_count = thread:r_u8() self.respawn_artefacts = thread:r_bool() self.forced_spawn = thread:r_bool() self.forced_spawn_override = thread:r_bool() self.forced_artefact = thread:r_stringZ() local layer_num = thread:r_u8() if(layer_num~=255) then self.cur_layer = "layer_"..layer_num end self.turned_off = thread:r_bool() set_save_marker(thread, "load", true, "anomaly_zone_binder") if (self.spawned_count < 0 or self.spawned_count > 200) then self:cleanup() end end